package ufrj.safcp.tools.fingerprint;

import java.awt.Image;
import java.io.File;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.griaule.grfingerjava.FingerprintImage;
import com.griaule.grfingerjava.GrFingerJava;
import com.griaule.grfingerjava.GrFingerJavaException;
import com.griaule.grfingerjava.IFingerEventListener;
import com.griaule.grfingerjava.IImageEventListener;
import com.griaule.grfingerjava.IStatusEventListener;
import com.griaule.grfingerjava.MatchingContext;
import com.griaule.grfingerjava.Template;

public class GriauleFingerprintHandler implements IFingerprintHandler,
		IStatusEventListener, IFingerEventListener, IImageEventListener {

	private static Logger logger = LoggerFactory.getLogger(GriauleFingerprintHandler.class);

	public static File GRIAULE_SDK_NATIVE_DIR;

	static {
		String griauleHome = System.getenv("GRIAULE_HOME");

		if (griauleHome != null) {
			GRIAULE_SDK_NATIVE_DIR = new File(griauleHome + File.separatorChar
					+ "bin");
		} else {
			throw new IllegalStateException(
					"A variavel de ambiente GRIAULE_HOME precisa ser definida para este projeto. "
							+ "Ela deve apontar para o Griaule Java SDK. Exemplo: C:\\Arquivos de programas\\Griaule\\Fingerprint SDK Java 2009. "
							+ "Reinicie o Eclipse após realizar a configuração.");
		}
		try {
			GrFingerJava.setNativeLibrariesDirectory(GRIAULE_SDK_NATIVE_DIR);
			GrFingerJava.setLicenseDirectory(GRIAULE_SDK_NATIVE_DIR);
		} catch (Exception e) {
			logger.error("Não foi possível inicializar o diretório do Griaule Java SDK");
		}
	}

	private static GriauleFingerprintHandler instance;

	public synchronized static GriauleFingerprintHandler getInstance() {
		if (instance == null) {
			instance = new GriauleFingerprintHandler();
		}
		return instance;
	}

	protected GriauleFingerprintHandler() {
		// Singleton
		try {
			// Somente para gerar o popup de trial no inicio
			new MatchingContext();
		} catch (GrFingerJavaException e) {
			// nothing
		} 
	}

	protected MatchingContext mc;
	protected IFingerprintReceiver receiver;

	private Template t;

	public void init(int threshold, IFingerprintReceiver receiver) {
		try {
			mc = new MatchingContext();
			mc.setVerificationThreshold(threshold);
			this.receiver = receiver;
		} catch (GrFingerJavaException e) {
			logger.error("Não foi possível criar o módulo de leitura da Griaule.");
			throw new IllegalStateException(e);
		}
	}

	public void destroy() {
		stop();
		mc = null;
	}
	
	private boolean started;

	public void start() {
		try {
			synchronized (this) {
				GrFingerJava.initializeCapture(this);
				this.started = true;
			}
		} catch (Exception e) {
			logger.error("Não foi possível iniciar o módulo de leitura da Griaule.");
			throw new IllegalStateException(e);
		}
	}

	public void stop() {
		synchronized (this) {
			if (this.started) {
				new Thread(new Runnable() {
					public void run() {
						try {
							// Fazendo isso numa thread pois o comando finalizeCapture() falha se o stop() for chamado
							// antes de um callback (onImageAcquired(), por exemplo) termine de executar
							GrFingerJava.finalizeCapture();
						} catch (GrFingerJavaException e) {
							logger.error("Não foi possível encerrar o módulo de leitura da Griaule.");
							throw new IllegalStateException(e);
						}
					}
				}).start();
				this.started = false;
			}
		}
	}

	public static String templateToString(Template template12) {

		byte[] array = template12.getData();
		StringBuffer s = new StringBuffer();

		for (int i = 0; i < array.length; i++) {
			s.append(array[i]).append('|');
		}

		s.insert(0, '|');

		return s.toString();
	}

	public void onSensorPlug(String idSensor) {
		try {
			if (logger.isDebugEnabled()) {
				logger.debug("capturando device " + idSensor);
			}
			GrFingerJava.startCapture(idSensor, this, this);
		} catch (GrFingerJavaException e) {
			throw new IllegalStateException(e);
		}
	}

	public void onSensorUnplug(String idSensor) {
		try {
			if (logger.isDebugEnabled()) {
				logger.debug("parando captura device " + idSensor);
			}
			GrFingerJava.stopCapture(idSensor);
		} catch (GrFingerJavaException e) {
			throw new IllegalStateException(e);
		}
	}

	public void onFingerDown(String arg0) {
		if (logger.isDebugEnabled()) {
			logger.debug("onFingerDown");
		}
	}

	public void onFingerUp(String arg0) {
		if (logger.isDebugEnabled()) {
			logger.debug("onFingerUp");
		}
	}

	public void startEnroll() {
		try {
			t = new Template(Template.FORMAT_ISO, new byte[0]);
			mc.startEnroll();
		} catch (GrFingerJavaException e) {
			logger.error("Não foi possível iniciar o processo de enroll.");
		}
	}

	public void stopEnroll() {
		t = null;
	}

	public boolean enroll(Image fingerprint, byte[] templateData) {
		try {
			if (t==null) throw new IllegalStateException("Método enroll() não pode ser chamado sem antes chamar startEnroll()");
			
			int enroll = mc.enroll((FingerprintImage) fingerprint, Template.FORMAT_ISO, t);
			
			switch (enroll) {
				case MatchingContext.GR_ENROLL_VERY_GOOD:
					logger.debug("ENROLL_VERY_GOOD");
					return true;
					
				case MatchingContext.GR_ENROLL_GOOD:
					logger.debug("ENROLL_GOOD");
					return true;
					
				case MatchingContext.GR_ENROLL_SUFFICIENT:
					logger.debug("ENROLL_SUFFICIENT");
					return true;
	
				case MatchingContext.GR_ENROLL_MAX_LIMIT_REACHED:
					logger.debug("ENROLL_MAX_LIMIT_REACHED");
					return false;
					
				case MatchingContext.GR_ENROLL_NOT_READY:
					logger.debug("ENROLL_NOT_READY");
					return false;

				default:
					logger.debug("Enroll inválido.");
					return false;
			}
				
		} catch (GrFingerJavaException e) {
			logger.error("Não foi possível iniciar o processo de enroll.");
			return false;
		}
	}
	

	public byte[] getEnrolledTemplate() {
		return t.getData();
	}

	public void onImageAcquired(String arg0, FingerprintImage arg1) {
		Template template;
		try {
			template = mc.extract(arg1, Template.FORMAT_ISO);
			receiver.receiveFingerprint(arg1, template.getData(), templateToString(template));
		} catch (GrFingerJavaException e) {
			logger.warn("Não foi possível extrair o template pelo modulo de leitura da Griaule.");
		}
	}

	public boolean match(byte[] templateData1, byte[] templateData2) {
		Template t1 = new Template(templateData1);
		Template t2 = new Template(templateData2);
		try {
			return mc.verify(t1, t2);
		} catch (Exception e) {
			logger.error("Não foi possível verificar os templates pelo modulo de leitura da Griaule.");
			throw new IllegalStateException(e);
		}
	}


}
